/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "quakedef.h"
#include "menu.shared.h"

#define MAX_MENU_STACK	256

m_state_t menu_stack[MAX_MENU_STACK];
int menu_stack_depth = 0;

qmenu_t *menus[MAX_MENUS];

int menu_ofsy = 0;

m_state_t m_state;
m_soundlevel_t m_soundlevel;

BOOL	m_recursiveDraw;

m_state_t	m_return_state;
BOOL		m_return_onerror;
char		m_return_reason[32];


/*
================
M_DrawCharacter

Draws one solid graphics character
================
*/
void M_DrawCharacter (int cx, int line, int num)
{
	Draw_Character (cx + ((vid.Width - 320) >> 1), line + menu_ofsy, num);
}

void M_Print (int cx, int cy, char *str)
{
	while (*str)
	{
		M_DrawCharacter (cx, cy, (*str) + 128);
		str++;
		cx += 8;
	}
}

void M_PrintJustified (int cx, int cy, char *str)
{
	M_Print (cx - strlen (str) * 8, cy, str);
}


void M_PrintWhite (int cx, int cy, const char *str)
{
	while (*str)
	{
		M_DrawCharacter (cx, cy, *str);
		str++;
		cx += 8;
	}
}


void M_DrawPic (int x, int y, qpic_t *pic)
{
	Draw_Pic (x + ((vid.Width - 320) >> 1), y + menu_ofsy, pic);
}


void M_DrawDivider (int y)
{
	char *divider = "\35\36\36\36\36\36\36\36\36\36\36\37";
	M_Print (160 - strlen (divider) * 4, y, divider);
}

void M_DrawBanner (char *name)
{
	M_DrawCenteredPic (0, Draw_CachePic (name));
	M_DrawDivider (28);
}


void M_DrawHighlight (int x, int y, int width)
{
	int colorramp[] = {66, 67, 68, 69, 70, 69, 68, 67};
	int color = colorramp[(int) (realtime * 10) % 8];

	Draw_Fill (x + ((vid.Width - 320) >> 1), (y + menu_ofsy) - 1, width, 10, color, 192);
}


void M_DrawHighlight (int y, int width)
{
	int colorramp[] = {66, 67, 68, 69, 70, 69, 68, 67};
	int color = colorramp[(int) (realtime * 10) % 8];

	Draw_Fill ((vid.Width - width) >> 1, (y + menu_ofsy) - 1, width, 10, color, 192);
}


void M_DrawCommand (int y, char *text)
{
	M_Print ((320 - strlen (text) * 8) >> 1, y, text);
}


void M_DrawCheckbox (int y, char *text, BOOL on)
{
	M_DrawStringOption (y, text, on ? "on" : "off");
}


void M_DrawValueList (int y, char *text, CMenuValueList &list)
{
	M_DrawStringOption (y, text, va ("%i", list.GetCurrent ()));
}


void M_DrawStringOption (int y, char *text, char *stringoption)
{
	if (text) M_PrintJustified (152, y, text);
	if (stringoption) M_PrintWhite (176, y, stringoption);
}


void M_DrawCenteredPic (int y, qpic_t *pic)
{
	M_DrawPic ((320 - pic->width) / 2, y, pic);
}


void M_DrawSmallCursor (int x, int y, int c)
{
	M_DrawCharacter (x, y, c + ((int) (realtime * 4) & 1));
}


void M_DrawTextBox (int x, int y, int width, int lines)
{
	Draw_TextBox (x + ((vid.Width - 320) >> 1), y + menu_ofsy, width, lines);
}

//=============================================================================

int m_save_demonum;

/*
================
M_ToggleMenu_f
================
*/
void M_ToggleMenu_f (void)
{
	if (key_dest == key_menu)
	{
		if (m_state != m_main)
		{
			menus[m_main]->EnterFunc ();
			return;
		}

		Key_SetDest (key_game);
		m_state = m_none;
		return;
	}

	if (key_dest == key_console)
		Con_ToggleConsole_f ();
	else menus[m_main]->EnterFunc ();
}


// register a NULL menu for the m_none case
class qmenu_none_t : public qmenu_t
{
public:
	qmenu_none_t (void) {this->Register (m_none);}
	void EnterFunc () {}
	BOOL KeyFunc (int key) {return TRUE;}
} menu_none;


void M_Init (void)
{
	// clear the sound level
	m_soundlevel = m_sound_none;

	// add menu shortcut commands
	Cmd_AddCommand ("help", M_Menu_Help_f);
	Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
	Cmd_AddCommand ("menu_main", M_Menu_Main_f);
	Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
	Cmd_AddCommand ("menu_options", M_Menu_Options_f);
	Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
	Cmd_AddCommand ("menu_load", M_Menu_Load_f);
	Cmd_AddCommand ("menu_save", M_Menu_Save_f);
	Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
	Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
	Cmd_AddCommand ("menu_video", M_Menu_Video_f);

	// add other commands
	Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
}


void M_AdjustCvar (cvar_t *var, float adj, float minval, float maxval)
{
	float value = var->value + adj;

	if (value > maxval)
		Cvar_SetValue (var, minval);
	else if (value < minval)
		Cvar_SetValue (var, maxval);
	else Cvar_SetValue (var, value);
}


/*
==========================================================================================
GENERIC OPTION
==========================================================================================
*/

BOOL qmenu_option_t::IsActive (void)
{
	return this->Active;
}


BOOL qmenu_option_t::IsVisible (void)
{
	return this->Visible;
}


void qmenu_option_t::SetTag (int newtag)
{
	this->Tag = newtag;
}


void qmenu_option_t::SetVisibleForTag (int tag, BOOL visible)
{
	if (this->Tag == tag) this->Visible = visible;
}


void qmenu_option_t::DrawHighlight (int y)
{
	// don't draw highlights on inactive options
	if (this->Active) M_DrawHighlight (y, 320);
}


/*
==========================================================================================
BIG SUBMENU - used for pictorial menus
==========================================================================================
*/

qmenu_bigsubmenu_t::qmenu_bigsubmenu_t (m_state_t submenu)
{
	this->SubMenu = submenu;
	this->Active = TRUE;
}

BOOL qmenu_bigsubmenu_t::Key (int key)
{
	if (key == K_ENTER)
	{
		// enter the submenu
		menus[this->SubMenu]->EnterFunc ();
		return TRUE;
	}
	else return FALSE;
}


void qmenu_bigsubmenu_t::DrawHighlight (int y)
{
	M_DrawPic (54, y, Draw_CachePic (va ("gfx/menudot%i.lmp", ((int) (realtime * 10) % 6) + 1)));
}


/*
==========================================================================================
STRING OPTION
==========================================================================================
*/

qmenu_stringoption_t::qmenu_stringoption_t (char *text, qmenu_stringoptionwatcher_t &watcher)
{
	Q_strncpy (this->Text, text, sizeof (this->Text) - 1);
	this->Watcher = &watcher;
	this->Active = TRUE;
}


int qmenu_stringoption_t::Draw (int y)
{
	M_PrintJustified (152, y, this->Text);
	M_PrintWhite (176, y, this->Watcher->GetFunc ());
	return y + 10;
}


BOOL qmenu_stringoption_t::Key (int key)
{
	switch (key)
	{
	case K_LEFTARROW:
		this->Watcher->SetFunc (-1);
		return TRUE;

	case K_RIGHTARROW:
		this->Watcher->SetFunc (1);
		return TRUE;

	default:
		return FALSE;
	}
}


void qmenu_stringoption_t::DrawHighlight (int y)
{
	M_DrawHighlight (y, 320);

	char *txt = this->Watcher->GetFunc ();
	int x = 164 + ((vid.Width - 320) >> 1);
	int c = 12 + ((int) (realtime * 4) & 1);

	Draw_BackwardsCharacter (x, y + menu_ofsy, c);
	Draw_Character (x + strlen (txt) * 8 + 16, y + menu_ofsy, c);
}


/*
==========================================================================================
TEXT WITH WHITE LABEL
==========================================================================================
*/

qmenu_textlabel_t::qmenu_textlabel_t (char *label, char *text)
{
	Q_strncpy (this->Text, label, sizeof (this->Text) - 1);
	this->DynamicText = text;
	this->Active = FALSE;
}


int qmenu_textlabel_t::Draw (int y)
{
	M_DrawStringOption (y, this->Text, this->DynamicText);
	return y + 10;
}


/*
==========================================================================================
DYNAMIC TEXT (can change as options change, always centered, always white)
==========================================================================================
*/

qmenu_dynamictext_t::qmenu_dynamictext_t (char *text)
{
	this->DynamicText = text;
	this->Active = FALSE;
}


int qmenu_dynamictext_t::Draw (int y)
{
	M_PrintWhite (160 - strlen (this->DynamicText) * 4, y, this->DynamicText);
	return y + 10;
}


/*
==========================================================================================
SLIDER
==========================================================================================
*/

qmenu_slider_t::qmenu_slider_t (char *text, char *var, float minval, float maxval, float step)
{
	// the var will not exist yet so we need to do it this way and Cvar_FindVar it at runtime
	Q_strncpy (this->Text, text, sizeof (this->Text) - 1);
	Q_strncpy (this->Var, var, sizeof (this->Var) - 1);

	this->MinVal = minval;
	this->MaxVal = maxval;
	this->Step = step;

	this->Active = TRUE;
}


int qmenu_slider_t::Draw (int y)
{
	float range = 0.0f;
	const int SLIDER_RANGE = 10;

	// the var will not exist at construction time so we need to do it this way
	cvar_t *var = Cvar_FindVar (this->Var);

	if (var)
	{
		if (this->Step > 0)
			range = (var->value - this->MinVal) / (this->MaxVal - this->MinVal);
		else range = (this->MaxVal - var->value) / (this->MaxVal - this->MinVal);
	}

	if (range < 0) range = 0;
	if (range > 1) range = 1;

	M_DrawStringOption (y, this->Text);
	M_DrawCharacter (168, y, 128);

	for (int i = 0; i < SLIDER_RANGE; i++)
		M_DrawCharacter (176 + i * 8, y, 129);

	M_DrawCharacter (176 + SLIDER_RANGE * 8, y, 130);
	M_DrawCharacter (176 + (SLIDER_RANGE - 1) * 8 * range, y, 131);

	return y + 10;
}


BOOL qmenu_slider_t::Key (int key)
{
	// the var will not exist at construction time so we need to do it this way
	cvar_t *var = Cvar_FindVar (this->Var);

	if (var)
	{
		// slide the slider
		if (key == K_ENTER || key == K_RIGHTARROW)
		{
			Cvar_SetValue (var, var->value + this->Step);

			if (var->value > this->MaxVal) Cvar_SetValue (var, this->MaxVal);
			if (var->value < this->MinVal) Cvar_SetValue (var, this->MinVal);

			return TRUE;
		}
		else if (key == K_LEFTARROW || key == K_BACKSPACE)
		{
			Cvar_SetValue (var, var->value - this->Step);

			if (var->value > this->MaxVal) Cvar_SetValue (var, this->MaxVal);
			if (var->value < this->MinVal) Cvar_SetValue (var, this->MinVal);

			return TRUE;
		}
		else return FALSE;
	}
	else return FALSE;
}


/*
==========================================================================================
SUBMENU
==========================================================================================
*/

qmenu_submenu_t::qmenu_submenu_t (char *text, m_state_t submenu)
{
	Q_strncpy (this->Text, text, sizeof (this->Text) - 1);
	this->SubMenu = submenu;
	this->Active = TRUE;
}


int qmenu_submenu_t::Draw (int y)
{
	M_DrawCommand (y, this->Text);
	return y + 10;
}


BOOL qmenu_submenu_t::Key (int key)
{
	if (key == K_ENTER)
	{
		// enter the submenu
		menus[this->SubMenu]->EnterFunc ();
		return TRUE;
	}
	else return FALSE;
}


/*
==========================================================================================
BIG ACTION - used for chunky pic menus
==========================================================================================
*/

qmenu_bigaction_t::qmenu_bigaction_t (void (*action) (void))
{
	this->Action = action;
	this->Active = TRUE;
}


BOOL qmenu_bigaction_t::Key (int key)
{
	if (key == K_ENTER && this->Action)
	{
		this->Action ();
		return TRUE;
	}
	else return FALSE;
}


void qmenu_bigaction_t::DrawHighlight (int y)
{
	M_DrawPic (54, y, Draw_CachePic (va ("gfx/menudot%i.lmp", ((int) (realtime * 10) % 6) + 1)));
}


/*
==========================================================================================
ACTION
==========================================================================================
*/

qmenu_action_t::qmenu_action_t (char *text, void (*action) (void))
{
	Q_strncpy (this->Text, text, sizeof (this->Text) - 1);
	this->Action = action;
	this->Active = TRUE;
}

int qmenu_action_t::Draw (int y)
{
	M_DrawCommand (y, this->Text);
	return y + 10;
}


BOOL qmenu_action_t::Key (int key)
{
	if (key == K_ENTER && this->Action)
	{
		this->Action ();
		return TRUE;
	}
	else return FALSE;
}


/*
==========================================================================================
CHECKBOX
==========================================================================================
*/

qmenu_checkbox_t::qmenu_checkbox_t (char *text, qmenu_checkboxwatcher_t &watcher)
{
	Q_strncpy (this->Text, text, sizeof (this->Text) - 1);
	this->Watcher = &watcher;
	this->Active = TRUE;
}


int qmenu_checkbox_t::Draw (int y)
{
	M_DrawStringOption (y, this->Text, this->Watcher->GetFunc () ? "on" : "off");
	return y + 10;
}


void qmenu_checkbox_t::DrawHighlight (int y)
{
	M_DrawHighlight (y, 320);

	if (this->Watcher->GetFunc ())
	{
		Draw_BackwardsCharacter (164 + ((vid.Width - 320) >> 1), y + menu_ofsy, 12 + ((int) (realtime * 4) & 1));
		Draw_Character (194 + ((vid.Width - 320) >> 1), y + menu_ofsy, 12 + ((int) (realtime * 4) & 1));
	}
	else
	{
		Draw_BackwardsCharacter (164 + ((vid.Width - 320) >> 1), y + menu_ofsy, 12 + ((int) (realtime * 4) & 1));
		Draw_Character (202 + ((vid.Width - 320) >> 1), y + menu_ofsy, 12 + ((int) (realtime * 4) & 1));
	}
}


BOOL qmenu_checkbox_t::Key (int key)
{
	switch (key)
	{
	case K_RIGHTARROW:
	case K_LEFTARROW:
		this->Watcher->SetFunc ();
		return TRUE;

	default:
		return FALSE;
	}
}


/*
==========================================================================================
DIVIDER
==========================================================================================
*/

qmenu_divider_t::qmenu_divider_t (void)
{
	this->Active = FALSE;
}


int qmenu_divider_t::Draw (int y)
{
	M_DrawDivider (y + 1);
	return y + 12;
}


/*
==========================================================================================
PLAQUE
==========================================================================================
*/

qmenu_plaque_t::qmenu_plaque_t (void)
{
	this->Active = FALSE;
}


int qmenu_plaque_t::Draw (int y)
{
	// doesn't advance y
	M_DrawPic (8, y - 20, Draw_CachePic ("gfx/qplaque.lmp"));
	return y;
}


/*
==========================================================================================
CHUNKY PIC
==========================================================================================
*/

qmenu_chunkypic_t::qmenu_chunkypic_t (char *name)
{
	Q_strncpy (this->Text, name, sizeof (this->Text) - 1);
	this->Active = FALSE;
}


int qmenu_chunkypic_t::Draw (int y)
{
	// doesn't advance y
	M_DrawPic (72, y, Draw_CachePic (this->Text));
	return y;
}


/*
==========================================================================================
BANNER
==========================================================================================
*/

qmenu_banner_t::qmenu_banner_t (char *name)
{
	Q_strncpy (this->Text, name, sizeof (this->Text) - 1);
	this->Active = FALSE;
}


int qmenu_banner_t::Draw (int y)
{
	qpic_t *p = Draw_CachePic (this->Text);

	M_DrawCenteredPic (y, p);
	return y + p->height + 2;
}


/*
==========================================================================================
TEXT AND NUMERIC BOXES
==========================================================================================
*/

qmenu_textbox_base_t::qmenu_textbox_base_t (char *label, char *textboxtext, int length)
{
	Q_strncpy (this->Text, label, sizeof (this->Text) - 1);
	this->TextboxText = textboxtext;
	this->Length = length;
	this->Active = TRUE;
}


int qmenu_textbox_base_t::Draw (int y)
{
	// regular draw
	M_DrawTextBox (168, y - 4, this->Length, 1);
	M_PrintJustified (152, y + 4, this->Text);
	M_PrintWhite (176, y + 4, this->TextboxText);

	// special handling if a highlight
	if (this->IsHighLight)
	{
		// do the input cursor
		M_DrawCharacter (176 + 8 * strlen (this->TextboxText), y + 4, 10 + ((int) (realtime * 4) & 1));

		// the next draw is not highlighted until we prove that it is
		this->IsHighLight = FALSE;
	}

	return y + 16;
}


void qmenu_textbox_base_t::DrawHighlight (int y)
{
	// do the highlight bar
	M_DrawHighlight (y + 4, 320);

	// flag special handling for the input cursor
	this->IsHighLight = TRUE;
}


BOOL qmenu_textbox_base_t::CommonKey (int key, int minkey, int maxkey)
{
	int l = strlen (this->TextboxText);

	switch (key)
	{
	case K_BACKSPACE:
		if (l)
			this->TextboxText[l - 1] = 0;

		// handled
		return TRUE;

	default:
		if (key < minkey || key > maxkey)
			break;

		if (l < this->Length)
		{
			this->TextboxText[l + 1] = 0;
			this->TextboxText[l] = key;
		}

		// handled
		return TRUE;
	}

	// not handled
	return FALSE;
}


/*
==========================================================================================
THE MAIN MENU OBJECT
==========================================================================================
*/

void qmenu_t::Register (m_state_t state)
{
	// find the first active option
	this->ActiveOption = 0;

	for (int i = 0; i < this->Options.size (); i++)
	{
		if (this->Options[i]->IsActive ())
		{
			this->ActiveOption = i;
			break;
		}
	}

	this->State = state;
	menus[state] = this;
}


void qmenu_t::Navigate (int dir)
{
	m_soundlevel = m_sound_navigate;

	if (this->Options.size () > 0)
	{
		int runover = 0;

		do
		{
			// advance by dir until we hit an option that's valid for activating
			this->ActiveOption += dir;

			if (this->ActiveOption < 0) this->ActiveOption = this->Options.size () - 1;
			if (this->ActiveOption >= this->Options.size ()) this->ActiveOption = 0;

			// prevent infinite loop if there are no active options in this menu
			if ((++runover) > this->Options.size ()) break;
		} while (!this->Options[this->ActiveOption]->IsActive () || !this->Options[this->ActiveOption]->IsVisible ());
	}
}


void qmenu_t::EnterMenu (void)
{
	if (menu_stack_depth < MAX_MENU_STACK)
	{
		// only push the stack if we're going to a different menu
		if (menu_stack[menu_stack_depth] != this->State)
		{
			menu_stack_depth++;
			Con_Printf (PRINT_DEVELOPER, "menu_stack_depth %i\n", menu_stack_depth);
			menu_stack[menu_stack_depth] = this->State;
		}
	}

	Key_SetDest (key_menu);
	m_state = this->State;
	m_soundlevel = m_sound_enter;
}


void qmenu_t::LeaveMenu (void)
{
	// go back to the previous menu, or m_none if there was no previous menu
	if (menu_stack_depth > 1)
	{
		menu_stack_depth--;
		Con_Printf (PRINT_DEVELOPER, "menu_stack_depth %i\n", menu_stack_depth);
		menus[menu_stack[menu_stack_depth]]->EnterFunc ();
	}
	else
	{
		// reset stack to 0 and stack 0 to the no-menu
		menu_stack_depth = 0;
		menu_stack[menu_stack_depth] = m_none;

		Key_SetDest (key_game);
		m_state = m_none;
		cls.demonum = m_save_demonum;

		if (cls.demonum != -1 && !cls.demoplayback && cls.state != ca_connected)
			CL_NextDemo ();
	}

	m_soundlevel = m_sound_enter;
}


void qmenu_t::AddOption (qmenu_option_t *option, int tag)
{
	// set the option tag
	option->SetTag (tag);

	if (option->IsActive ())
	{
		// add to a list of active options???
	}

	this->Options.push_back (option);
}


void qmenu_t::ShowOptions (int tag)
{
	for (int i = 0, y = 0; i < this->Options.size (); i++)
		this->Options[i]->SetVisibleForTag (tag, TRUE);
}


void qmenu_t::HideOptions (int tag)
{
	for (int i = 0, y = 0; i < this->Options.size (); i++)
		this->Options[i]->SetVisibleForTag (tag, FALSE);
}


void qmenu_t::Draw (void)
{
	// drawfunc runs first and allows a custom draw action for each menu
	this->CustomDraw ();

	// draw the options
	for (int i = 0, y = 0; i < this->Options.size (); i++)
	{
		// only draw visible options
		if (this->Options[i]->IsVisible ())
		{
			if (i == this->ActiveOption)
				this->Options[i]->DrawHighlight (y);

			y = this->Options[i]->Draw (y);
		}
	}
}


void qmenu_t::Key (int key)
{
	// escape always overrides everything else and gets out of the current menu
	if (key == K_ESCAPE)
	{
		this->LeaveMenu ();
		return;
	}

	// let a custom key function override first
	if (this->KeyFunc (key)) return;

	// some menus have no options
	if (!this->Options.size ()) return;

	// let the active option try to use the key
	if (this->Options[this->ActiveOption]->Key (key))
	{
		// only change the sound if it hasn't already been changed
		// don't switch it for the enter key because that's also used for e.g. new game from the sp menu
		// and doing so causes a small click from the end of the adjust sound on entry to a map
		if (m_soundlevel == m_sound_none && key != K_ENTER)
			m_soundlevel = m_sound_adjust;

		return;
	}

	// send it through default processing
	switch (key)
	{
	case K_LEFTARROW:
	case K_UPARROW:
		this->Navigate (-1);
		break;

	case K_RIGHTARROW:
	case K_DOWNARROW:
		this->Navigate (1);
		break;

	default:
		break;
	}
}


void M_Draw (void)
{
	// always evaluate as the sbar needs it for the rankings screen
	menu_ofsy = vid.Height >> 3;

	if (m_state == m_none || key_dest != key_menu)
	{
		// reset stack to 0 and stack 0 to the no-menu
		menu_stack_depth = 0;
		menu_stack[menu_stack_depth] = m_none;
	}
	else
	{
		if (!m_recursiveDraw)
		{
			if (scr.ConCurrent)
				Draw_ConsoleBackground (1.0f);
			else if (!scr.DrawDialog)
				Draw_FadeScreen ();
		}
		else m_recursiveDraw = FALSE;

		// then the generic draw runs
		menus[m_state]->Draw ();

		if (cls.state == ca_connected) Con_DrawNotify ();
	}

	// update the menu sound
	switch (m_soundlevel)
	{
	case m_sound_quit:
		S_LocalSound ("zombie/z_gib.wav");
		break;

	case m_sound_adjust:
		S_LocalSound ("misc/menu3.wav");
		break;

	case m_sound_enter:
		S_LocalSound ("misc/menu2.wav");
		break;

	case m_sound_navigate:
		S_LocalSound ("misc/menu1.wav");
		break;

	case m_sound_alert:
		S_LocalSound ("misc/talk.wav");
		break;

	default:
	case m_sound_none:
		// no sound
		break;
	}

	// clear the sound after playing it
	m_soundlevel = m_sound_none;
}


bool M_ValueListSortFunc (int a, int b)
{
	return (a - b) < 0;
}


void CMenuValueList::Clean (void)
{
	if (this->dirty)
	{
		std::sort (this->values.begin (), this->values.end (), M_ValueListSortFunc);
		this->dirty = FALSE;
	}
}

CMenuValueList::CMenuValueList (void)
{
	this->values.clear ();
	this->dirty = FALSE;
	this->option = 0;
}

BOOL CMenuValueList::Find (int val)
{
	this->Clean ();

	for (int i = 0; i < this->values.size (); i++)
	{
		if (this->values[i] == val)
		{
			this->option = i;
			return TRUE;
		}
	}

	return FALSE;
}

int CMenuValueList::GetCurrent (void)
{
	this->Clean ();
	return this->values[this->option];
}

void CMenuValueList::Adjust (int dir)
{
	this->Clean ();
	this->option += dir;

	if (this->option < 0) this->option = this->values.size () - 1;
	if (this->option >= this->values.size ()) this->option = 0;
}

void CMenuValueList::SetCvar (cvar_t *var)
{
	this->Clean ();
	Cvar_SetValue (var, this->values[this->option]);
}

void CMenuValueList::Append (int val)
{
	// try to find the option
	if (this->Find (val)) return;

	// now append the new value
	this->values.push_back (val);
	this->dirty = TRUE;

	// finally find the newly added value
	this->Find (val);
}


void M_DrawMapshot (int x, int y, char *name)
{
	Draw_Mapshot (x + ((vid.Width - 320) >> 1), y + menu_ofsy, name);
}


char *M_GetSkill (int sk)
{
	if (sk == 0)
		return "Easy";
	else if (sk == 1)
		return "Normal";
	else if (sk == 2)
		return "Hard";
	else return "NIGHTMARE!";
}


char *M_GetSkill (void)
{
	return M_GetSkill (skill->integer);
}


void M_DrawLabelValue (int labelx, int valuex, int y, char *label, char *value)
{
	M_Print (labelx, y, label);
	M_PrintWhite (valuex, y, value);
}

